var crypto  = require('crypto');
var keyLen  = 8;
var ciphers = "des";
var config = {
    "id":"$id",
    "uid":"uid",         //用户唯一标识
    "hkey":"cookie",
    "idType":"cookie",     //SESSION ID存储方式
    "lockTime":500,
    "lockNums":10,
    "dbase":null
};
exports.config = config;
//cookie
exports.create = function(req,res){
    return new session(req,res);
}

var cookieID={
    "random":function(){
        var buf = crypto.randomBytes(keyLen);
        var str =  buf.toString('hex');
        return str.substr(0,keyLen);
    },
    "encode":function(uid) {
        var secret = cookieID.random();
        var cipher = crypto.createCipher(ciphers, secret);
        var enc = cipher.update(uid,'utf8','hex');
        enc += cipher.final('hex');
        return secret + enc;
    },
    "decode":function(id) {
        if (!id || id.length <= keyLen) {
            return false;
        }
        var secret = id.substr(0, keyLen);
        var str = id.substr(keyLen);
        try {
            var decipher = crypto.createDecipher(ciphers, secret);
            var dec = decipher.update(str, 'hex', 'utf8');
            dec += decipher.final('utf8');
            return dec;
        }
        catch (e){
            return false;
        }
    }
};

var session = function (req,res) {
    var self = this;
    var id,uid,ischk;
    var $cache    = new redis(req,res);
    var $idType   = Array.isArray(config['idType']) ? config['idType'] : [config['idType']];
    var $lockTime = config['lockTime'];
    var $lockNums = config['lockNums'];
    var $lastReqTime = '$lastReqTime';

    var getCookie
    //检查数据有效性
    var check = function (keys,callback) {
        var data;
        var delayNum = 0;
        var success = function($nowTime){
            if($lockTime>0){
                $cache.set(uid,$lastReqTime,$nowTime);
            }
            if(Array.isArray(keys)){
                callback(null,data);
            }
            else{
                callback(null,data[keys] || null);
            }

        }

        var delay = function () {
            delayNum ++;
            var $Date  = new Date();
            console.log('locked uid:'+uid+' num:'+delayNum+' time:'+ $Date.toLocaleString());
            if ( delayNum > $lockNums) {
                return callback('cookie_locked');
            }
            $cache.get(uid,$lastReqTime,function(err,$last){
                if(err){
                    return setTimeout(delay, $lockTime);
                }
                var $nowTime = $Date.getTime();
                var $diff =  $nowTime- $last;
                if( $diff > $lockTime){
                    success($nowTime);
                }
                else{
                    setTimeout(delay, $lockTime);
                }
            });
        }

        //安全检查
        var validity  = function(){
            //检查
            var $userid   = config.uid;
            var $cookieid = config.id;

            if ( id != data[$cookieid] || uid != data[$userid] ) {
                return callback("login_failed");
            }
            //乐观锁
            var $nowTime  = new Date().getTime();
            if ($lockTime > 0 && ( $nowTime - data[$lastReqTime]) < $lockTime ) {
                setTimeout(delay, $lockTime);
            }
            else {
                success($nowTime);
            }
        }
        //GET COOKIE ID
        var getCookieID = function(type){
            if(type=='cookie'){
                if(!res['cookie']){
                    res['cookie']= require('./cookie').create(req,res);
                }
                return res['cookie'].get(config.id);
            }
            else{
                return $.dataSelect(res.query,config.id);
            }
        }

        if(Array.isArray(config.idType)){
            for(var k in config.idType){
                var type = config.idType[k];
                id = getCookieID(type);
                if(id){
                    break;
                }
            }
        }
        else{
            id = getCookieID(config.idType);
        }
        if(!id){
            return callback("login_failed","cookie id empty");
        }
        //GET USER ID
        uid = cookieID.decode(id);
        if(!uid){
            return callback("login_failed","cookie id error");
        }
        //获取数据
        var $k = Array.isArray(keys) ? keys : [keys];
        $k.push($lastReqTime);
        $k.push(config['id']);
        $k.push(config['uid']);
        $cache.get(uid,$k,function(err,ret){
            if(err){
                return callback(err,ret);
            }
            else if(!ret){
                return callback('login_failed','cookie_not_exist');
            }
            data = ret;
            validity();
        });
    }
    //登录
    this.login = function (key,data, callback) {
        if(typeof callback!='function'){
            callback = $.callback;
        }
        var $nowTime  = new Date().getTime();
        id   = cookieID.encode(key);
        uid  = key;
        data[config.id]   = id;
        data[config.uid]  = uid;
        data[$lastReqTime] = $nowTime;
        $cache.set(uid,data,function(err,ret){
            if(err){
                return callback(err,ret);
            }
            else{
                if($idType.inArray("cookie")){

                }
                return callback(false,id);
            }
        });
    }

    this.set = function (key,val, callback) {
        $cache.set(uid,key,val,callback);
    }
    //获取登录信息
    this.get = function (keys,callback) {
        if(!ischk){
            ischk = true; check(keys,callback);
        }
        else{
            $cache.get(uid,keys,callback);
        }
    }

    this.del = function(key,callback){
        if(!key){
            key = config.id;
        }
        $cache.del(uid,key,callback);
    }

    //设置临时锁定状态
    this.lockTime=function(ms){
        $lockTime = ms;
    }

}



//redis
var redis = function (req,res) {
    var self = this;

    var getRedisConn = function (callback) {
        var $dbase = typeof config.dbase == 'function' ?  config.dbase(req,res) : config.dbase;
        if(!$dbase){
            return callback("session_error", "config dbase empty" );
        }
        $.redis($dbase, function (err, ret) {
            if (err) {
                return callback(err, ret);
            }
            return callback(null, ret.hash() );
        });
    }

    var hash = function (uid) {
        return config.hkey+'|' + uid;
    }

    this.get = function (uid, keys, callback) {
        var $hash = hash(uid);
        getRedisConn(function (err, conn) {
            if (err) {
                return callback(err, conn);
            }
            conn.get($hash, keys, callback)
        });
    }

    this.set = function (uid, key, val, callback) {
        if(typeof val=='function'){
            callback = val;
            val = null;
        }
        else if(typeof callback != 'function'){
            callback = $.callback;
        }
        var $hash = hash(uid);

        getRedisConn(function (err, conn) {
            if (err) {
                return callback(err, conn);
            }
            conn.set($hash, key, val, callback);
        });
    }

    this.del = function (uid, keys, callback) {
        var $hash = hash(uid);
        getRedisConn(function (err, conn) {
            if (err) {
                return callback(err, conn);
            }
            conn.del($hash, keys, callback);
        });
    }

}